Skip to content

fix(musicxml): import string indications on notation staves as string number annotations - #2913

Merged
Danielku15 merged 9 commits into
developfrom
fix/musicxml-string-annotation
Oct 6, 2026
Merged

Danielku15 merged 9 commits into
developfrom
fix/musicxml-string-annotation

Conversation

@Danielku15

Copy link
Copy Markdown
Member

Note

AI-authored disclosure (alphatab-ai-authored-v1)

Portions of this content were authored by an AI agent. The agent has read
AGENTS.md and the human submitter accepts responsibility for
compliance with the rules in that document.

Issues

Fixes #2822

Proposed changes

A <technical><string> on a staff without tuning is a string indication for the player (e.g. a circled string number in classical guitar music), not a tab position. The importer converted it using the empty staff tuning, which stored invalid strings (0 or negative). Combined with a <fret>, the note became a stringed note with a wrong pitch and NaN MIDI ticks, which silenced playback.

alphaTab now treats a string without a usable tab position as an annotation on the pitched note. Playback and standard notation keep using the written pitch, and no tab is created.

MusicXML import

  • No tuning: the string is stored relative to a standard 6-string instrument (note.string) and note.showStringNumber is set. A <fret> is ignored with a warning because it has no meaning without a tuning.
  • Tuning present, <string> without <fret>: string number annotation.
  • Tuning present, <string> + <fret>: tab position as before.
  • Strings outside the available range are dropped with a warning.

Rendering

  • String number annotations are also painted for pitched notes, not only for stringed notes.
  • New internal Note.getStringCount(staff): the tuning length, or 6 for staves without tuning. The importer, the glyph and alphaTex all use it, so conversions agree.

alphaTex

  • The string note property gets an overload with a value for pitched notes, e.g. a4{string 1} (1 = highest string, same numbering as fret.string). This keeps the new model state roundtrip-safe.
  • On fretted or percussion notes the value is rejected with the new diagnostic AT221, since their string comes from fret.string. Out-of-range values report AT211.
  • The exporter writes string N for pitched notes. The language definitions are regenerated from packages/alphatex.

Related fixes found while testing

  • GP7 export changed the input score. To write string/fret for pitched staves, GpifWriter assigns fingerings to the notes and temporarily sets a default tuning. Only the tuning was restored. Afterwards the score played wrong pitches, and a second export wrote a file without tuning and with wrong MIDI values. Gp7Exporter now exports a copy of the score created via the JSON serializer; the backing track audio is shared with the copy because it is not serialized.
  • MusicXML: every bar had an additional empty voice (regression from fix(musicxml gp-exporter): optimized handling of more than 4 voices #2831). The first voice mapped on a staff now uses the voice each bar is created with, instead of inserting a new one.
  • GP7 exporter tests: the folder roundtrips now only include Guitar Pro files (.gp3, .gp4, .gp5, .gpx, .gp). This replaces the individual MusicXML exclusions, since MusicXML differs too much from the Guitar Pro model for a full roundtrip.

Known limitations (not addressed here)

  • On GP7 export, the fingering assignment picks its own strings for pitched notes, so string number annotations do not carry over to the GP file.
  • A string-only note on a tab staff with tuning still renders oddly on the tab staff.

Tests

  • MusicXML importer: string-only and string + fret, each on staves with and without tuning; out-of-range strings; no non-finite MIDI ticks.
  • alphaTex importer: {string N} on pitched notes, AT211 / AT221 diagnostics, export roundtrip.
  • New visual test string-numbers-pitched (MusicXML) plus a dedicated alphaTex roundtrip of it.
  • GP7 exporter: exporting leaves the score unchanged and a repeated export produces the same file.
  • Updated references:
    • 32a-Notations.png now shows the string number of the note marked "string".
    • Four MusicXML importer snapshots no longer contain the empty trailing voice.

Checklist

  • I consent that this change becomes part of alphaTab under its current or any future open source license
  • This PR is linked to an accepted issue (see above)
  • Changes are implemented
  • New tests were added
  • I have read AGENTS.md if an AI helped draft any part of this PR

AI authorship disclosure

  • No AI agent authored any part of this PR (description, code, tests, or commit messages)
  • An AI agent contributed to this PR. The AI-authored disclosure block
    (alphatab-ai-authored-v1) is present at the top of this body, and I have
    personally reviewed every change and can explain each one

Further details

  • This is a breaking change
  • This change will require update of the documentation/website

String numbers on notes without a fret (e.g. classical guitar string
indications on a standard notation staff) were never painted because the
annotation required a stringed note. The string number glyph also mapped
the string through the staff tuning which is empty on such staves.

Introduce Note.getStringCount which falls back to a standard 6 string
instrument for staves without tuning and use it for the annotation.

Refs #2822
Extend the string note property with an overload accepting the string
number (1 is the highest string) to describe string number annotations
on pitched notes, e.g. 'a4{string 1}'. The exporter writes this form for
pitched notes so the model roundtrips.

The value is only allowed on pitched notes (new diagnostic AT221);
fretted notes keep specifying their string via 'fret.string'.

Refs #2822
…annotation

A <technical><string> on a staff without tuning was converted using an
empty tuning which resulted in invalid string values (0 or negative).
In combination with a <fret> the note became a stringed note with wrong
pitch and invalid MIDI ticks, silencing the playback.

On staves without tuning the string is now treated as string number
annotation on the pitched note (assuming a standard 6 string instrument),
frets are ignored. Strings without fret on tuned staves are annotations
too. Strings outside the available strings are dropped.

Fixes #2822
Adds a MusicXML visual test with string number annotations on a
standard notation staff and verifies the alphaTex roundtrip of it.
Updates the 32a-Notations reference which now shows the string number.

Refs #2822
To write string and fret information for pitched staves, the GPIF writer
assigns fingerings to the notes and temporarily sets a default tuning on
the staff. Only the tuning was restored afterwards, leaving notes with
string and fret on staves without tuning. This resulted in wrong pitches
on playback after an export and broken files on repeated exports.

The exporter now works on a copy of the score created via the JSON
serializer.

Refs #2822
Bars are created with an initial voice, but the first MusicXML voice
mapped on a staff inserted an additional voice instead of using it,
leaving an empty trailing voice in every bar.

Refs #2822
The folder roundtrips listed MusicXML files individually as exceptions
because they differ too much from the Guitar Pro data model. Only
include supported Guitar Pro file extensions instead.

Refs #2822
@Danielku15
Danielku15 enabled auto-merge (squash) October 5, 2026 20:52
@Danielku15
Danielku15 merged commit fb4813f into develop Oct 6, 2026
7 of 13 checks passed
@Danielku15
Danielku15 deleted the fix/musicxml-string-annotation branch October 6, 2026 09:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MusicXML: score is completely silent when a <technical><string> appears on a standard notation staff

1 participant